<?php

/*! 
 * @page NavigationPage Исходный код класса Navigation 
 * @include navigation.class.php
 */

/*! @addtogroup ClassWebFormsQA Основные классы проекта
 *  Группа включает основные классы используемые в проекте WebFormsQA
 *  @{
 */


/**
 * @class Navigation
 * @brief Базовый класс навигации по элементам страницы\n
 * @ref NavigationPage
 */
class Navigation {

    /** ссылка на обьект базы данных 
     * @var object $base
     */
    var $base;

    /** 
     * @brief конструктор, который сохраняет в классе ссылку на обьект базы данных
     * @param object &$database	- ссылка на объект работающий с базой данных MySql <br><br> 
     * @ref StructMySql
     */
    function Navigation(&$database)
    {
        $this->base = &$database;
    }

    /**
     * @brief Метод на основании кол-ва входных элементов разбивает элементы
     *        на части, для последовательного вывода вида:
     * @code       
     *        [1-4][5-8][след][показать все]
     *        [prev] 1 2 3 4 … [next]
     * @endcode       
     *
     * @param integer $a        - количество элементов в массиве, для которого необходима навигация
     * @param integer $offset   - текущее смещение в массиве, будут показаты элементы [$offset ... $offset+$q])
     * @param integer $q        - количество элементов, которые будут показаны на странице
     * @param string  $path     - ссылка на страницу (т.е.  "index.php?categoryID=1&")
     * @param string  $out      - ссылка на результат работы
     *
     * ПРИМЕЧАНИЕ:  смещение должно быть кратно q  например q = 4 тогда  0  4  8  12 и т.д.
     *
     * @return bool             - успех операции
     */
    function getNavigator($a, $offset, $q, $path, &$out)
    {

        $flag_result = false;

        if ($offset === -1) {
            // выделяем ситуацию, когда смещение не кратное, для отметки ссылки показать все
            $tail           = 0;
            $flag_show_all  = true;
            $offset         = 0;
        } else {
            $tail           = $offset % $q;
            $flag_show_all  = false;
        }

        // если все элементы не помещаются на страницу и смещение правильное
        if ($a > $q && !$tail ) {

            $flag_result = true;

            //[prev]
            if ($offset>0) $out .= "<a href=\"".$path."offset=".($offset-$q)."\">[".STRING_PREVIOUS."]</a> &nbsp;";

            // цифровая ссылка
            $k = $offset / $q;

            // не более четырех ссылок слева
            $min = $k - 5;
            if ($min < 0) { $min = 0; }
            else {
                if ($min >= 1) {
                    // ссылка на первой странице
                    $out .= "<a href=\"".$path."offset=0\">[1-".$q."]</a> &nbsp;";
                    if ($min != 1) { $out .= "... &nbsp;"; };
                }
            }

            for ($i = $min; $i<$k; $i++) {
                $m = $i*$q + $q;
                if ($m > $a) $m = $a;

                $out .= "<a href=\"".$path."offset=".($i*$q)."\">[".($i*$q+1)."-".$m."]</a> &nbsp;";
            }

            // текущая страница
            if (!$flag_show_all) { // strcmp($offset, "show_all"))
                $min = $offset+$q;
                if ($min > $a) $min = $a;
                $out .= "[".($k*$q+1)."-".$min."] &nbsp;";
            } else {
                $min = $q;
                if ($min > $a) $min = $a;
                $out .= "<a href=\"".$path."offset=0\">[1-".$min."]</a> &nbsp;";
            }

            // не более четырех ссылок справа
            $min = $k + 5;
            if ($min > $a/$q) { $min = $a/$q; };
            for ($i = $k+1; $i<$min; $i++)
            {
                $m = $i*$q+$q;
                if ($m > $a) $m = $a;

                $out .= "<a href=\"".$path."offset=".($i*$q)."\">[".($i*$q+1)."-".$m."]</a> &nbsp;";
            }

            $n_all = $a/$q;
            if ($min*$q < $a) { // последняя ссылка
                if ($min*$q < $a-$q) $out .= " ... &nbsp;";
                if( !($a%$q == 0) ) {
					$out .= "<a href=\"".$path."offset=".($a-$a%$q)."\">[".($a-$a%$q+1)."-".$a."]</a> ";                		
                } else {
                	$out .= "<a href=\"".$path."offset=".($a-$q)."\">[".($a-$q+1)."-".$a."]</a> ";
                }
                
            }
            
            //[next]
            if ($offset<$a-$q) $out .= "<a href=\"".$path."offset=".($offset+$q)."\">[".STRING_NEXT."]</a>";

            //[show all]
            if (!$flag_show_all) { // strcmp($offset, "show_all")) {
                $out .= " <a href=\"".$path."offset=-1\">[".STRING_SHOWALL."]</a>";
            } else {
                // выделена ссылка показать все
                $out .= " [".STRING_SHOWALL."]";
            }

        }

        return $flag_result;
    }

    /**
     * @brief Метод запрашивает дерево категорий в котором выделена
     *            только одна запрашиваемая категория (все под дерево)
     *            и все корневые категории, например 
     * @code           
     *                 КАТЕГОРИЯ1
     *                     категория1_1
     *                     категория1_2
     *                         категория1_2_1
     *                КАТЕГОГИЯ2
     *                КАТЕГОРИЯ3
     * @endcode               
     *            данное дерево будет использоваться для отображения навигации
     *
     * @param integer $curr     - идентификатор выбранной категории
     * @param string  $name     - название таблицы в базе данных, которая должна включать поля:
     * -                            categoryID - Идентификатор
     * -                            name       - название категории
     * -                            parent     - идентификатор родительской категории
     * @param array  &$out      - результат работы, массив содержащий дерево категорий
     *                            в формате (categoryID, name, level)
     *
     * @return bool             - успех операции
     */
    function getTreeCategories($sel, $name, &$out)
    {
        settype($sel, "integer");
        $flag_result= false;
        $categoryID = $sel;
        $path       = array($sel);
        $base       = &$this->base;

        do {
            $q = $base->db_query("
                SELECT parent
                FROM ".CATEGORIES_TABLE."
                WHERE categoryID='$sel'"
            ) or die ($base->db_error());

            $row    = $base->db_fetch_row($q);
            $sel    = $row ? $row[0] : 0; // получаем родительский идентификатор
            $path[] = $sel;

        } while ($sel);

        // берем обратный путь
        $path = array_reverse($path);

        $flag_result = $this->_processCategories(0, $path, $categoryID, $name, $out);

        return $flag_result;

    } // function getTreeCategories

    /**
     * @brief Метод выбирает диапазон элементов из входного массива
     *        и формируем массив результата
     *
     * @param integer $begin    - индекс начального элемента (c 0)
     * @param integer $end      - индекс элемента следующего за конечным ( не входит в результат )
     * @param array   &$in      - входной массив, ключи первой размерности должны быть цифровыми
     * @param integer $size     - размер входного массива
     * @param array &$out       - ссылка на массив результата
     *
     * ПРИМЕЧАНИЕ:  в случае если начальный индекс равен -1
     *              входной массив будет целиком скопирован на выход через ссылку
     *              т.е. результатом может быть или массив out или in
     *
     * @return                  - ссылка на массив результата
     */
    function &selectRange($begin, $end, &$in, $size, &$out)
    {
        $result = &$out;

        if ($begin === -1) {
            $result = &$in;

        } elseif ($begin < $end && $begin < $size ) {
            // проверяем корректность задания индексов

            for($i=$begin, $j=0; $i < $end && $i < $size; $i++, $j++) {
                $out[$j] = $in[$i];
            }
        }

        return $result;
    } // selectRange


    /**
     * @brief Метод формирует один уровень дерева категорий, формально данный
     *        метод является закрытым и может использоваться только самим классом
     *
     * @param integer $level    - текущий уровень, 0 - для главной категории
     *                            1 для всех подкатегорий и т.д.
     * @param string  $path     - путь от корневой категории до выбранной
     * @param integer $sel      - идентификатор выбранной категория
     * @param string  $name     - название таблицы в базе данных, которая должна включать поля
     * -                            categoryID - Идентификатор
     * -                            name       - название категории
     * -                            parent     - идентификатор родительской категории
     * @param array &$out       - ссылка на массив результата содержащий строки (categoryID, name, level)
     *
     * ПРИМЕЧАНИЕ:  дерево категорий будет прокручено начиная от указанного пути, но не полностью,
     *              пока метод включает тело запроса MySql, который лучше перенести в класс работающий 
     *              с базой данных 
     *
     * @return bool     - успех операции
     */
    function _processCategories($level, $path, $sel, $name, &$out)
    {
        $flag_result    = false;
        $base           = &$this->base;
        $cnt            = 0;

        $q = $base->db_query("
            SELECT categoryID, name
            FROM ".$name."
            WHERE parent='$path[$level]'
            ORDER BY name"
        ) or die ($base->db_error());

        while ($row = $base->db_fetch_row($q)) {
           $out[$cnt][0] = $row[0];
           $out[$cnt][1] = $row[1];
           $out[$cnt][2] = $level;
           $cnt++;

           // имеются в наличии подкатегории
           if ($level+1 < count($path) && $row[0] == $path[$level+1]) {

              $sub_out = array();
              // вызываем под дерево рекурсивно
              if (Navigation::_processCategories($level+1, $path, $sel, $name, $sub_out)) {
                // добавляем подкатегории в конец массива результата,
                for ($j=0; $j < count($sub_out); $j++) {
                   $out[] = $sub_out[$j];
                   $cnt++;
                }
              }
           }
        }

        if ($cnt) $flag_result = true;

        return $flag_result;
    }


} // end class Navigation

/*! @} */

?>
